Mac OS Sound

| Previous | Chapter Contents | Chapter Top |

Sound Manager Routines

The following are new Sound Manager routines added since the release of Sound Manager 3.0.

SndGetInfo and SndSetInfo

extern pascal OSErr SndGetInfo(
                     SndChannelPtr chan, OSType selector,
                     void *infoPtr);
extern pascal OSErr SndSetInfo(
                     SndChannelPtr chan, OSType selector,
                     const void *infoPtr);

The two new routines SndGetInfo and SndSetInfo are used to get and set information about the sound environment. Both routines use a selector based interface similar to the SPBGetDeviceInfo and SPBSetDeviceInfo routines found in the Sound Input Manager, and in fact they use the same sound information selectors.

SndGetInfo and SndSetInfo operate on an open Sound Manager channel, and can be used to retrieve and change information about the channel, including hardware settings. These routines should be used instead of attempting to communicate directly with sound components.

These new calls are only available with Sound Manager version 3.1 or later. Check for this by calling SndSoundManagerVersion for the installed version. Note that you can always open a sound channel for the hardware device that you desire by passing kUseOptionalOutputDevice as the synth parameter and the component reference as the init parameter.

OSErr OpenChannel(OSType myType)
{
    ComponentDescription   searching;
    Component              outputDevice;
    OSErr                  err;

    // search for a sound output device component
    searching.componentType = kSoundOutputDeviceType;
    searching.componentSubType = myType;
    searching.componentManufacturer = kAnyComponentManufacturer;
    searching.componentFlags = 0;
    searching.componentFlagsMask = kAnyComponentFlagsMask;
    outputDevice = nil;
    outputDevice = FindNextComponent(outputDevice, &searching);

    if (outputDevice == nil)
        err = cantFindHandler; /*component not found*/
    else
    {
        gChan = nil;
        err = SndNewChannel(&gChan, kUseOptionalOutputDevice,
                            (long)outputDevice, nil);
    }
    return (err);
}

For example, to determine the current hardware sampling rate of the given sound channel you may use this code:

UnsignedFixed sampleRate;
err = SndGetInfo(gChan, siSampleRate, &sampleRate);
GetSoundOutputInfo() and SetSoundOutputInfo()
pascal OSErr GetSoundOutputInfo(Component outputDevice, OSType selector,
                                void *infoPtr);
pascal OSErr SetSoundOutputInfo(Component outputDevice, OSType selector,
                                const void *infoPtr);

These two routines get and set information about the sound environment: GetSoundOutputInfo and SetSoundOutputInfo . Both routines use a selector based interface similar to the SPBGetDeviceInfo and SPBSetDeviceInfo routines found in the Sound Input Manager, and in fact they use the same sound info selectors.

GetSoundOutputInfo and SetSoundOutputInfo operate directly on a sound output device, and can be used to retrieve and change information about the hardware settings. These routines should be used instead of attempting to communicate directly with sound output components. Setting the output device parameter to nil causes the default output device to be used. These calls are similar to GetSndInfo and SetSndInfo but do not require an opened sound channel. For example, to determine the sampling rate of the sound hardware on the default output device, you could use this code:

OSErr GetCurrentSampleRate(UnsignedFixed *sampleRate)
    {   
OSErr err;
    err = GetSoundOutputInfo(nil, siSampleRate, sampleRate);
return (err);
}

ParseAIFFHeader

pascal OSErr ParseAIFFHeader(
                     short fRefNum, SoundComponentData *sndInfo,
                                         unsigned long *numFrames,
                     unsigned long *dataOffset);

The ParseAIFFHeader routine returns information describing the audio data in the given AIFF file. The fRefNum parameter specifies the open AIFF file to use. The sndInfo parameter is a SoundComponentData structure that returns the following information about the format of the sound in the AIFF file:

flags
Always returns 0

format
The sound format (i.e., 'raw', 'twos', 'MAC3', etc.)

numChannels
The number of channels (i.e., 1 = mono, 2 = stereo)

sampleSize
The sample size (i.e., 8 = 8-bit, 16 = 16-bit)

sampleRate
The sampling rate (in samples/second)

sampleCount
The number of audio samples in the file

buffer
Always returns 0

reserved
Always returns 0

The numFrames parameter returns the number of frames of audio data in the file, and the dataOffset parameter returns the byte offset of the first audio sample in the file.

ParseSndHeader

pascal OSErr ParseSndHeader(
                     SndListHandle sndHandle,
                                         SoundComponentData *sndInfo,
                     unsigned long *numFrames,
                                         unsigned long *dataOffset);

The ParseSndHeader routine returns information describing the audio data in the given 'snd ' resource handle. The sndHandle parameter specifies the sound handle to use. The sndInfo parameter is a SoundComponentData structure that returns the following information about the format of the sound in the handle:

flags
Always returns 0

format
The sound format (i.e., 'raw', 'twos', 'MAC3', etc.)

numChannels
The number of channels (i.e., 1 = mono, 2 = stereo)

sampleSize
The sample size (i.e., 8 = 8-bit, 16 = 16-bit)

sampleRate
The sampling rate (in samples/second)

sampleCount
The number of audio samples in the file

buffer
Always returns 0

reserved
Always returns 0

The numFrames parameter returns the number of frames of audio data in the handle, and the dataOffset parameter returns the byte offset of the first audio sample in the handle.

GetCompressionInfo

pascal OSErr GetCompressionInfo(
                     short compressionID, OSType format,
                                         short numChannels, short sampleSize, CompressionInfoPtr cp);

For a given AIFF file or snd resource, the information contained within it might be used to determine basic characteristics of the sound such as its duration.

duration = numSampleFrames / sampleRate

Note that this is a valid calculation for an uncompressed sound. But this calculation returns an incorrect result for a compressed sound. The problem here is that each sample frame in a compressed sound is composed of one or more packets rather than sample points (see "Sampled-Sound Data" ), and each packet in that compressed sound can itself represent several sample points. We therefore need a way to determine the number of samples in a packet in order to get an accurate calculation.

The compressionID parameter defines the compression algorithm used on the sample. The AIFF-C Extended Common Chunk does not contain a compressionID field. In this case (and when using snd resources where the OSType describing the compression format is known) you should always pass the constant fixedCompression in this parameter and the OSType in the format parameter. The format field will then contain the OSType representing the compression format. If you set the compressionID field in a compressed sound header to any value other than fixedCompression , then the format field is set to zero. The format parameter is the OSType describing the format of the compressed sound data. If you pass the constant fixedCompression in the compressionID parameter you will need to pass a valid compression type here. Some of the valid format types are:

NONE - sound is not compressed
MAC3 - compression format is MACE 3:1
MAC6 - compression format is MACE 6:1
ima4 - compression format is IMA 4:1

There are some snd resources that do not store an OSType in the format field of the compressed sound header describing the compression format. You can still use GetCompressionInfo in this case by passing in the compressionID and passing 0 in the format parameter. The correct OSType will be returned in the format field of the CompressionInfo structure. Using the appropriate fields from an AIFF-C Extended Common Chunk or our snd resource compressed sound header, we can make the call to GetCompressionInfo :

Example Extended Common Chunk

    myExtendedCommonChunk.ckID = 'COMM';
    myExtendedCommonChunk.ckSize = 34;
    myExtendedCommonChunk.numChannels = 1;
    myExtendedCommonChunk.numSampleFrames = 7633;
    myExtendedCommonChunk.sampleSize = 8;
    myExtendedCommonChunk.sampleRate = 22254.54545;
    myExtendedCommonChunk.compressionType = MAC3;
    myExtendedCommonChunk.compressionName = "MACE 3-to-1";

Example Compressed Sound Header

    myCompressedSoundHeader.samplePtr = nil;
    myCompressedSoundHeader.numChannels = 1;
    myCompressedSoundHeader.sampleRate = rate22khz;
    myCompressedSoundHeader.encode = cmpSH;
    myCompressedSoundHeader.numFrames = 7633
    myCompressedSoundHeader.format = 0;
    myCompressedSoundHeader.compressionID = threeToOne;
    myCompressedSoundHeader.packetSize = threeToOnePacketSize;
    myCompressedSoundHeader.snthID = 0;
    myCompressedSoundHeader.sampleSize = 8;

// fill in the CompressionInfo from our Extended Common Chunk

OSErr           err;
CompressionInfo cmpInfo;


err = GetCompressionInfo(fixedCompression,
                        (OSType)(myExtendedCommonChunk.compressionType),
                        myExtendedCommonChunk.numChannels,
                        myExtendedCommonChunk.sampleSize,
                        &cmpInfo);

// fill in the CompressionInfo from our Compressed Sound Header

OSErr           err;
CompressionInfo cmpInfo;

err = GetCompressionInfo(myCmpSoundHeader->compressionID,
                            myCmpSoundHeader->format,
                            myCmpSoundHeader->numChannels,
                            myCmpSoundHeader->sampleSize,
                            &cmpInfo);

Note that this call will work for all sound formats, compressed or uncompressed.

Using GetCompressionInfo , the Sound Manager will do the right thing. You get back the information you need in the CompressionInfo struct, with no special casing needed. Upon returning from the call to GetCompressionInfo , you have a filled CompressionInfo struct.

recordSize = 20
format = MAC3
compressionID = threeToOne
samplesPerPacket = 6
bytesPerPacket = 2
bytesPerFrame = 2
bytesPerSample = 1

Now you can use this information to determine the duration of our sound.

duration = (numSampleFrames * samplesPerPacket) / sampleRate

By substituting the data given from the example above, you get the following results.

2.06 seconds = (7633 * 6) / 22254.54545

By including the CompressionInfo struct you should never need to special case code for compressed vs. uncompressed sounds--and all sound data calculations should be correct.

The following is a list of useful calculations that can be made using the data returned in the struct, along with data from our Extended Common Chunk or Compressed Sound Header:

seconds = (numFrames * samplesPerPacket) / sampleRate;
samples = numFrames * samplesPerPacket;
bytes = numFrames * bytesPerFrame;
compressionRatio = (samplesPerPacket * bytesPerSample) / bytesPerPacket;

GetCompressionName

pascal OSErr GetCompressionName(
                     OSType compressionType,
                     Str255 compressionName);

The GetCompressionName routine returns a string describing the given compression format in a string that can be displayed to the user. The compressionType parameter specifies the compression format, and the name is returned in compressionName .

This string can be used in pop-up menus and other user interface elements to allow the user to select a compression format.

SoundConverterGetBufferSizes

pascal OSErr SoundConverterGetBufferSizes(
                     SoundConverter sc,
                                         unsigned long targetBytes,
                     unsigned long *inputFrames,
                                         unsigned long *inputBytes,
                     unsigned long *outputBytes);

SoundConverterGetBufferSizes is used to determine the input and output buffer sizes for a given target size. This is so you can make sure your buffers will fit the conversion parameters established with SoundConverterOpen .

The targetBytes parameter is the approximate number of bytes you would like both your input and output buffers to be. The inputFrames and inputBytes parameters return the actual size you should make your input buffer, in frames and bytes, respectively. The outputBytes parameter returns the size in bytes for your output buffer.

The returned input and output buffer sizes can be larger than your target size settings. This is because they are rounded up depending on the format, but they will be very close to the target settings. Also note that the input and output sizes may be very different, depending on the input and output formats given in SoundConverterOpen . The sizes are calculated assuming you will convert all data in the input buffer to the output buffer.

SoundConverterBeginConversion

pascal OSErr SoundConverterBeginConversion(SoundConverter sc);

SoundConverterBeginConversion starts a conversion. All state information is reset to default values in preparation for a new input buffer.

This routine can be called at interrupt time.

SoundConverterConvertBuffer

pascal OSErr SoundConverterConvertBuffer(
                     SoundConverter sc,
                                         const void *inputPtr, unsigned long inputFrames,
                                         void *outputPtr, unsigned long *outputFrames,
                                         unsigned long *outputBytes);

SoundConverterConvertBuffer converts a buffer of data from the input format to the output format. The inputPtr parameter points to the input data, and inputFrames gives the number of frames in that buffer. The outputPtr parameter specifies where the output data should be placed. The outputFrames and outputBytes parameters return the number of frames and bytes placed in the output buffer respectively.

This routine will consume all the data in the input buffer. Depending on the complexity of the conversion, however, not all the converted data may be put in the output buffer right away. The SoundConverterEndConversion routine is used to flush out all this remaining data before a conversion session is closed.

If you are using this routine in conjunction with SoundConverterGetBufferSizes , it is very important that you do not pass in a value in inputFrames larger than the frames value returned by SoundConverterGetBufferSizes , or you will overflow your output buffer. The SoundConverterConvertBuffer calls converts ALL the input data! This routine can be called at interrupt time.

SoundConverterEndConversion

pascal OSErr SoundConverterEndConversion(
                     SoundConverter sc,
                     void *outputPtr,
                     unsigned long *outputFrames,
                     unsigned long *outputBytes);

SoundConverterEndConversion ends a conversion. Any data remaining in the converters is flushed out and returned here.

This routine can be called at interrupt time.

 


© 1998 Apple Computer, Inc.

| Previous | Chapter Contents | Chapter Top |